package com.study.springsecurity.security.checks;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Security注解检查
 * 检查接口是否存在security权限注解，对于没有添加权限注解的接口，限制其访问
 */
@Slf4j
@Component
public class SecurityAnnotationCheck {

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    // 接口必须被修饰注解
    static Set<Class<? extends Annotation>> securityAnnotationSet = new HashSet<>();

    static {
        securityAnnotationSet.add(RolesAllowed.class);
        securityAnnotationSet.add(PermitAll.class);
        securityAnnotationSet.add(DenyAll.class);

        securityAnnotationSet.add(Secured.class);

        securityAnnotationSet.add(PreAuthorize.class);
    }

    public boolean hasSecurityAnnotation(HttpServletRequest request, Authentication auth) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("SecurityAnnotationCheck hasSecurityAnnotation RequestURI：" + requestURI);

        // 通过请求查询到其处理方法，检查方法或者类上是否有权MyAccessDeniedHandler handle error denied限校验注解，若没有则不能访问
        HandlerExecutionChain handler = requestMappingHandlerMapping.getHandler(request);
        if (Objects.isNull(handler)) {
            throw new RuntimeException("错误的请求地址");
        }
        Set<Class<? extends Annotation>> classAndMethodAnnotations = new HashSet<>();

        Method method = ((HandlerMethod) handler.getHandler()).getMethod();
        Annotation[] annotations = method.getAnnotations();
        classAndMethodAnnotations.addAll(Arrays.stream(annotations).map(Annotation::annotationType).collect(Collectors.toSet()));

        Class<?> declaringClass = method.getDeclaringClass();
        annotations = declaringClass.getAnnotations();
        classAndMethodAnnotations.addAll(Arrays.stream(annotations).map(Annotation::annotationType).collect(Collectors.toSet()));

        classAndMethodAnnotations.retainAll(securityAnnotationSet);
        if (CollectionUtils.isEmpty(classAndMethodAnnotations)) {
            throw new AccessDeniedException("接口未添加权限，禁止所有人访问");
        }
        return true;
    }

}